package project.layer.physical;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;


public class Path {

	private LinkedList<Crossroad> totalPath;
	private LinkedList<Integer> destinationsIDs;

	public Path() {
		this(new LinkedList<Crossroad>(), new LinkedList<Integer>());
		
	}
	
	/**
	 * Only use when no destinations are in the path.
	 */
	public Path(List<Crossroad> list){
		this(list, new LinkedList<Integer>());
	}

	@SuppressWarnings("unchecked")
	public Path(List<Crossroad> list, List<Integer> destinationIDs) {
		super();
		this.totalPath = new LinkedList<Crossroad>(list);
		this.destinationsIDs = new LinkedList<Integer>(destinationIDs);
		
		
	}

	public long getDistanceToLastPickupPoint(){
		long result = 0;
		boolean atLastPickUpPoint = false;
		int idOfLastPickUpPoint = getDestinations().get(getDestinations().size()-2);
		for(int i = 0; !atLastPickUpPoint; i++){
			Road road = totalPath.get(i).getConnectionTo(totalPath.get(i+1));
			result += road.getLength();
			if(totalPath.get(i+1).hasPickUpPoint() && totalPath.get(i+1).getPickUpPoint().getCorrespondingAgent().getId() == idOfLastPickUpPoint){
				atLastPickUpPoint = true;
			}
		}
		return result;
	}

	
	public Path getReverse(){
		LinkedList<Crossroad> reverse = new LinkedList<Crossroad>();
		Iterator<Crossroad> it = getListOfCrossroadsPath().descendingIterator();
		while(it.hasNext()){
			reverse.add(it.next());
		}
		LinkedList<Integer> reverseDestinations = new LinkedList<Integer>();
		Iterator<Integer> it2 = getDestinations().descendingIterator();
		while(it.hasNext()){
			reverseDestinations.add(it2.next());
		}
		return new Path(reverse, reverseDestinations);
	}
	
	public Crossroad getFirstCrossroadOfPath(){
		return totalPath.getFirst();
	}
	
	public void add(Path path){
		totalPath.addAll(path.getListOfCrossroadsPath());
		for(Integer destination : path.getDestinations()){
			if(destinationsIDs.contains(destination)){
				throw new IllegalArgumentException("Dit pad bevat reeds de bestemmingen van het meegegeven pad!");
			}
		}
		destinationsIDs.addAll(path.getDestinations());
	}
	public void addDestinations(List<Integer> destinations){
		destinationsIDs.addAll(destinations);
	}
	public void removeCrossroadsTill(Crossroad cr){
		boolean found = false;
		while(!found){
			if(totalPath.getFirst().equals(cr)){
				found = true;
			}
			else{
				totalPath.removeFirst();
			}
		}
	}
	
	public LinkedList<Integer> getDestinations(){
		
		return (LinkedList<Integer>)destinationsIDs.clone();
	}
	
	public void addToDestinations(int packagePointAgentID){
		destinationsIDs.add(packagePointAgentID);
	}
	


	public void addCrossroadToPath(Crossroad cr){
		if(cr == null)
			throw new IllegalArgumentException("Crossroad must not be null");
		if(!totalPath.isEmpty())
			if(!totalPath.get(totalPath.size()-1).isConnected(cr))
				throw new IllegalArgumentException("Crossroad must be reachable from the last crossroad in the path");
		this.totalPath.add(cr);
	}

	protected void setTotalPath(LinkedList<Crossroad> path) {
		this.totalPath = path;
	}

//	public Path reset(){
//		this.index = 0;
//		return this;
//	}
	
	

	public Path getClone(){
		return new Path(totalPath,destinationsIDs);
	}

//	public boolean hasNext() {
//		return index < totalPath.size()-1;
//	}

	public void removeFirstCrossroadFromPath(){
		Crossroad cr = totalPath.removeFirst();
		
	}
	
	public void removeVisitedDestination(int destinationID){
		destinationsIDs.remove((Integer)destinationID);
	}
	
	public long getTotalDistance(){
		long result = 0;
		for(int i = 0; i < this.totalPath.size() - 1; i++){
			Road road = totalPath.get(i).getConnectionTo(totalPath.get(i+1));
			if(road != null){
				result += road.getLength();
			}
			
		}
		return result;
	}

	public boolean pathInList(ArrayList<Path> rejectedPaths) {
		boolean matchFound = false;
		for(Path r : rejectedPaths){
			if(r.getTotalDistance() == this.getTotalDistance() && r.totalPath.size() == this.totalPath.size()){
				matchFound = true;
				for(int i=0;i<r.totalPath.size();i++){
					if(!r.totalPath.get(i).equals(this.totalPath.get(i))){
						matchFound = false;
						break;
					}
				}
			}
		}
		return matchFound;
	}
	
	public void addCrossroadToFrontOfPath(Crossroad crossroad){
		totalPath.addFirst(crossroad);

	}


	public Crossroad getFollowingCrossroad(Crossroad crossroad){
		return getCrossroadXHopsFurther(crossroad, 0);
	}
	
	
	public Crossroad getCrossroadXHopsFurther(Crossroad crossroad, int hopsToBeSkipped){
		for(int i = 0; i < this.totalPath.size(); i++){
			if(this.totalPath.get(i).equals(crossroad))
				if(i+1+hopsToBeSkipped < this.totalPath.size()){
					return this.totalPath.get(i+1+hopsToBeSkipped);
				}
				else{
					return null;
				}
				
		}
		return null;
	}
	
	public Path getPathTillFirstDropOffPoint(){
		Path result = new Path();
		boolean reached = false;
		LinkedList<Crossroad> crs = getListOfCrossroadsPath();
		for(int i = 0; !reached ; i++){
			Crossroad cr = crs.get(i);
			if(cr.hasDropOffPoint() && getDestinations().contains(cr.getDropOffPoint().getCorrespondingAgent().getId())){
				result.addCrossroadToPath(cr);
				result.addToDestinations(getDestinations().get(0));
				reached = true;
			}
			else{
				result.addCrossroadToPath(cr);
				
			}
		}
		return result;
	}


	public void insertPath(List<Crossroad> pathToBeInserted){
		Crossroad first = pathToBeInserted.get(0);
		Crossroad second = pathToBeInserted.get(pathToBeInserted.size()-1);
		LinkedList<Crossroad> result = getCrossroadListFromStartTo(first);
		result.addAll(pathToBeInserted);
		result.addAll(getCrossroadListStartingFrom(second));
		this.totalPath = result;
	}
	
	/**
	 *  @note Does NOT include the crossroad specified as parameter!
	 */
	private LinkedList<Crossroad> getCrossroadListStartingFrom(Crossroad cr) {
		int index = this.totalPath.indexOf(cr);
		LinkedList<Crossroad> result;
		if( index != -1){
			result = new LinkedList<Crossroad>();
			for(int i = index +1 ; i < this.totalPath.size(); i++){
				result.add(this.totalPath.get(i));
			}
			return result;
		}else{
			return null;
		}
	}
	
	
	
	/**
	 *  @note Does NOT include the crossroad specified as parameter!
	 */
	private LinkedList<Crossroad> getCrossroadListFromStartTo(Crossroad cr) {
		int index = this.totalPath.indexOf(cr);
		LinkedList<Crossroad> result;
		if( index != -1){
			result = new LinkedList<Crossroad>();
			for(int i = 0; i < index; i++){
				result.add(this.totalPath.get(i));
			}
			return result;
		}else{
			return null;
		}
	}

	public boolean contains(Crossroad connector) {
		return this.totalPath.contains(connector);
	}
	
	public LinkedList<Crossroad> getListOfCrossroadsPath(){
		return (LinkedList<Crossroad>)totalPath.clone();
	}
	@Override
	public String toString() {
		String path = "Path with crossroads ";
		Iterator<Crossroad> it = totalPath.iterator();
		while(it.hasNext()){
			path += it.next().getId();
			if(it.hasNext()){
				path += ", ";				
			}
		}
		path += " with length: " + totalPath.size(); 
		return path;
	}
}
